package net.maku.generator.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.NamingCase;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import net.maku.generator.common.exception.ServerException;
import net.maku.generator.common.page.PageResult;
import net.maku.generator.common.service.impl.BaseServiceImpl;
import net.maku.generator.config.GenDataSource;
import net.maku.generator.config.template.TemplateInfo;
import net.maku.generator.dao.ProjectDao;
import net.maku.generator.domain.entity.ProjectEntity;
import net.maku.generator.domain.entity.TableEntity;
import net.maku.generator.domain.entity.TemplateEntity;
import net.maku.generator.domain.entity.TemplateGroupEntity;
import net.maku.generator.domain.model.ProjectDataModel;
import net.maku.generator.domain.query.Query;
import net.maku.generator.domain.vo.PreviewVO;
import net.maku.generator.domain.vo.TreeVO;
import net.maku.generator.enums.TemplateGroupTypeEnum;
import net.maku.generator.enums.TemplateTypeEnum;
import net.maku.generator.service.*;
import net.maku.generator.utils.GenUtils;
import net.maku.generator.utils.TemplateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import static cn.hutool.core.date.DatePattern.PURE_DATETIME_PATTERN;

/**
 * 项目管理Service
 *
 * @author 阿沐 babamu@126.com
 * <a href="https://maku.net">MAKU</a>
 */
@Service
public class ProjectServiceImpl extends BaseServiceImpl<ProjectDao, ProjectEntity> implements ProjectService {

    @Autowired
    private TemplateGroupService templateGroupService;

    @Autowired
    private DataSourceService dataSourceService;

    @Autowired
    private TableService tableService;

    @Autowired
    private GeneratorService generatorService;

    @Autowired
    private TemplateService templateService;

    @Override
    public PageResult<ProjectEntity> page(Query query) {
        IPage<ProjectEntity> page = baseMapper.selectPage(getPage(query), getWrapper(query));

        return new PageResult<>(page.getRecords(), page.getTotal());
    }

    @Override
    public void download(Long id) {
        List<PreviewVO> previewList = buildProjectPreviewList(id);

        //写入到文件中
        for (PreviewVO previewVO : previewList) {
            Integer templateType = previewVO.getTemplateType();
            // 生成文件
            if (Objects.equals(templateType, TemplateTypeEnum.FILE.getCode())) {
                FileUtil.writeUtf8String(previewVO.getContent(), previewVO.getFilePath());
            } else if (Objects.equals(templateType, TemplateTypeEnum.DIRECTORY.getCode())) {
                // 生成目录
                FileUtil.mkdir(previewVO.getFilePath());
            }
        }
    }

    @Override
    public void downloadZip(Long id, HttpServletResponse response) throws IOException {
        ProjectEntity project = baseMapper.selectById(id);

        List<PreviewVO> previewList = buildProjectPreviewList(id);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        for (PreviewVO previewVO : previewList) {
            try {
                // 添加到zip
                zip.putNextEntry(new ZipEntry(previewVO.getFilePath()));
                IoUtil.writeUtf8(zip, false, previewVO.getContent());
                zip.flush();
                zip.closeEntry();
            } catch (IOException e) {
                throw new ServerException("模板写入失败：" + previewVO.getFilePath(), e);
            }
        }
        IoUtil.close(zip);

        // zip压缩包数据
        byte[] data = outputStream.toByteArray();

        String dateTime = DateUtil.format(new Date(), PURE_DATETIME_PATTERN);
        response.reset();

        String projectName = project.getProjectName();
        response.setHeader("Content-Disposition", "attachment; filename=" + projectName + "_" + dateTime + ".zip");
        response.addHeader("Content-Length", "" + data.length);
        response.setContentType("application/octet-stream; charset=UTF-8");

        IoUtil.write(response.getOutputStream(), false, data);
    }

    @Override
    public List<PreviewVO> preview(Long projectId) {
        return buildProjectPreviewList(projectId).stream()
                //只显示文件
                .filter(previewVO -> Objects.equals(previewVO.getTemplateType(), TemplateTypeEnum.FILE.getCode()))
                .toList();
    }

    @Override
    public List<TreeVO> treeData(Long projectId) {
        List<PreviewVO> previewList = buildProjectPreviewList(projectId);

        List<TreeVO> treeList = new ArrayList<>();
        // 渲染模板并输出
        for (PreviewVO previewVO : previewList) {
            TreeVO treeVO = new TreeVO();
            treeVO.setFilePath(previewVO.getFilePath());
            treeVO.setLabel(previewVO.getFileName());
            treeVO.setTemplateId(previewVO.getTemplateId());
            treeList.add(treeVO);
        }

        return buildTree(treeList);
    }

    private List<TreeVO> buildTree(List<TreeVO> treeList) {
        Map<String, TreeVO> nodeMap = new HashMap<>();

        for (TreeVO treeVO : treeList) {
            String[] pathParts = treeVO.getFilePath().split("/");
            for (int i = 0; i < pathParts.length; i++) {
                StringBuilder pathBuilder = new StringBuilder();
                for (int j = 0; j <= i; j++) {
                    pathBuilder.append(pathParts[j]);
                    if (j < i) {
                        pathBuilder.append("/");
                    }
                }
                String path = pathBuilder.toString();
                TreeVO tempTreeVO = nodeMap.get(path);
                if (tempTreeVO == null) {
                    tempTreeVO = new TreeVO();
                    tempTreeVO.setLabel(pathParts[i]);
                    tempTreeVO.setFilePath(path);
                    tempTreeVO.setLevel(i);
                    // 判断是否是文件
                    if (i == pathParts.length - 1) {
                        tempTreeVO.setTemplateId(treeVO.getTemplateId());
                        tempTreeVO.setIsFile(true);
                    } else {
                        tempTreeVO.setIsFile(false);
                    }
                }
                nodeMap.put(path, tempTreeVO);
            }
        }

        List<TreeVO> values = new ArrayList<>(nodeMap.values());
        return values.stream()
                //找到层级为0的节点
                .filter(treeVO -> treeVO.getLevel() == 0)
                //添加子节点
                .peek(treeVO -> treeVO.setChildren(getChildren(treeVO, values)))
                //进行排序
                .sorted((tree1, tree2) -> {
                    if (tree1.getIsFile() && !tree2.getIsFile()) {
                        return 1;
                    } else if (!tree1.getIsFile() && tree2.getIsFile()) {
                        return -1;
                    } else {
                        return tree1.getLabel().compareTo(tree2.getLabel());
                    }
                })
                .toList();
    }

    private List<TreeVO> getChildren(TreeVO node, List<TreeVO> treeList) {
        return treeList.stream()
                .filter(treeVO -> treeVO.getFilePath().startsWith(node.getFilePath()))
                .filter(treeVO -> node.getLevel() + 1 == treeVO.getLevel())
                //递归添加子节点
                .peek(treeVO -> treeVO.setChildren(getChildren(treeVO, treeList)))
                //进行排序
                .sorted((tree1, tree2) -> {
                    if (tree1.getIsFile() && !tree2.getIsFile()) {
                        return 1;
                    } else if (!tree1.getIsFile() && tree2.getIsFile()) {
                        return -1;
                    } else {
                        return tree1.getLabel().compareTo(tree2.getLabel());
                    }
                })
                .toList();
    }

    @Override
    public TemplateInfo generatorCode(Long projectId, Long tableId, Long templateId, Integer templateGroupType) {
        if (Objects.equals(templateGroupType, TemplateGroupTypeEnum.PROJECT.getCode())) {
            ProjectEntity project = getById(projectId);

            //获取数据源信息
            GenDataSource dataSource = dataSourceService.get(project.getDatasourceId());

            TemplateEntity template = templateService.getById(templateId);

            //获取项目预览数据
            ProjectDataModel projectDataModel = buildProjectDataModel(project, dataSource);
            projectDataModel.setTemplateName(template.getTemplateName());
            //内容
            String fileContent = TemplateUtils.getContent(template.getTemplateContent(), projectDataModel);
            //路径
            String filePath = TemplateUtils.getContent(template.getGeneratorPath(), projectDataModel);
            //文件名
            String fileName = filePath.substring(filePath.lastIndexOf("/") + 1);

            TemplateInfo templateInfo = new TemplateInfo();
            templateInfo.setTemplateName(template.getTemplateName());
            templateInfo.setTemplateData(fileContent);
            templateInfo.setGeneratorPath(filePath);
            templateInfo.setFileName(fileName);
            return templateInfo;
        } else {
            return generatorService.generatorCode(tableId, templateId);
        }
    }

    @Override
    public List<TableEntity> tableList(Long projectId, String tableName) {
        ProjectEntity project = baseMapper.selectById(projectId);
        Long datasourceId = project.getDatasourceId();
        // 获取数据源
        GenDataSource datasource = dataSourceService.get(datasourceId);
        // 根据数据源，获取全部数据表
        List<TableEntity> tableList = GenUtils.getTableList(datasource, tableName);
        tableList.forEach(table -> {
            LambdaQueryWrapper<TableEntity> queryWrapper = Wrappers.lambdaQuery(TableEntity.class);
            queryWrapper.eq(TableEntity::getProjectId, projectId);
            queryWrapper.eq(TableEntity::getTableName, table.getTableName());
            boolean exists = tableService.exists(queryWrapper);
            table.setExist(exists);
        });

        return tableList;
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void delete(List<Long> idList) {
        baseMapper.deleteBatchIds(idList);
        Wrapper<TableEntity> queryWrapper = Wrappers.lambdaQuery(TableEntity.class)
                .in(TableEntity::getProjectId, idList);
        tableService.remove(queryWrapper);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void saveData(ProjectEntity entity) {
        baseMapper.insert(entity);
        //默认导入该数据源中的所有表
        List<TableEntity> tableList = tableList(entity.getId(), null);
        if (CollUtil.isEmpty(tableList)) {
            return;
        }
        for (TableEntity tableEntity : tableList) {
            tableService.tableImport(entity.getId(), tableEntity.getTableName());
        }
    }

    /**
     * 构建渲染数据
     */
    private ProjectDataModel buildProjectDataModel(ProjectEntity project, GenDataSource dataSource) {
        //项目数据
        ProjectDataModel projectDataModel = new ProjectDataModel();
        projectDataModel.setProjectName(project.getProjectName());
        projectDataModel.setProjectNameUnderline(StrUtil.replace(project.getProjectName(), "-", "_"));
        projectDataModel.setProjectNamePascal(NamingCase.toPascalCase(projectDataModel.getProjectNameUnderline()));
        projectDataModel.setProjectPackage(project.getProjectPackage());
        projectDataModel.setProjectPackageSlash(projectDataModel.getProjectPackage().replace(".", "/"));
        projectDataModel.setProjectVersion(project.getProjectVersion());
        projectDataModel.setBackendPath(project.getBackendPath());
        projectDataModel.setFrontendPath(project.getFrontendPath());
        projectDataModel.setProjectDesc(project.getProjectDesc());

        //数据源数据
        projectDataModel.setDatabaseDriverClassName(dataSource.getDbType().getDriverClass());
        projectDataModel.setDataBaseUrl(dataSource.getConnUrl());
        projectDataModel.setDataBaseUsername(dataSource.getUsername());
        projectDataModel.setDataBasePassword(dataSource.getPassword());

        return projectDataModel;
    }

    public List<PreviewVO> buildProjectPreviewList(Long id) {
        ProjectEntity project = baseMapper.selectById(id);

        //获取项目模板组信息
        Long projectTemplateGroupId = project.getProjectTemplateGroupId();
        TemplateGroupEntity templateGroup = templateGroupService.getById(projectTemplateGroupId);
        List<TemplateEntity> projecTemplateList = templateGroup.getTemplateList();

        //查询该项目下的表
        List<TableEntity> tableList = tableService.list(new QueryWrapper<TableEntity>()
                .eq("project_id", project.getId()));

        //获取表预览数据
        List<PreviewVO> previewList = new ArrayList<>();
        for (TableEntity tableEntity : tableList) {
            List<PreviewVO> tempList = generatorService.preview(tableEntity.getId());
            previewList.addAll(tempList);
        }

        //获取数据源信息
        GenDataSource dataSource = dataSourceService.get(project.getDatasourceId());

        //获取项目预览数据
        for (TemplateEntity template : projecTemplateList) {
            ProjectDataModel projectDataModel = buildProjectDataModel(project, dataSource);
            projectDataModel.setTemplateName(template.getTemplateName());
            Integer templateType = template.getTemplateType();
            //内容
            String fileContent;
            PreviewVO previewVO = new PreviewVO();
            if (TemplateTypeEnum.FILE.getCode().equals(templateType)) {
                fileContent = TemplateUtils.getContent(template.getTemplateContent(), projectDataModel);
                previewVO.setIsFile(true);
            } else {
                fileContent = "";
                previewVO.setIsFile(false);
            }
            //路径
            String filePath = TemplateUtils.getContent(template.getGeneratorPath(), projectDataModel);
            //文件名
            String fileName = filePath.substring(filePath.lastIndexOf("/") + 1);
            previewVO.setContent(fileContent);
            previewVO.setFileName(fileName);
            previewVO.setFilePath(filePath);
            previewVO.setTemplateId(template.getId());
            previewVO.setTemplateGroupType(templateGroup.getType().getCode());
            previewVO.setTemplateType(templateType);
            previewList.add(previewVO);
        }
        return previewList;
    }

}